
package com.hlkj.pay.app.merchant.ext.impl.helipay;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.core.type.TypeReference;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.framework.http.template.OkHttpTemplate;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantProfitOrderExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.profit.ChannelProfitOrderContext;
import com.hlkj.pay.app.merchant.ext.dto.profit.ChannelProfitOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.impl.helipay.constant.HeliPayConstant;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.HeliPayAppChannelParams;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.HeliPayConfig;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.SignAndEncryptedContent;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.req.DelayedSplitApplyReq;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.req.DelayedSplitQueryReq;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.req.DelayedSplitReturnQueryReq;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.req.DelayedSplitReturnReq;
import com.hlkj.pay.app.merchant.ext.impl.helipay.dto.res.*;
import com.hlkj.pay.app.merchant.ext.impl.helipay.ext.HeliPaySceneCommonPayOrder;
import com.hlkj.pay.app.merchant.ext.impl.helipay.handler.HeliPayMerchantCommonParamsHandle;
import com.hlkj.pay.app.merchant.ext.impl.helipay.util.HeliPaySignUtil;
import com.hlkj.pay.dto.order.PayOrderFundDto;
import com.hlkj.pay.dto.order.RefundOrderFundDto;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAppChannelDO;
import com.hlkj.pay.infrastructure.model.order.OrderFundDetailDO;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.pay.PayChannelRequestDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.DateUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author HlpayTeam
 * @date 2025/01/06 17:47
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_HELIPAY)
public class HeliPayMerchantProfitOrderExtPt extends HeliPaySceneCommonPayOrder implements ChannelMerchantProfitOrderExtPt {

    private final OkHttpTemplate okHttpTemplate;

    @Autowired
    private HeliPayMerchantCommonParamsHandle heliPayMerchantCommonParamsHandle;

    @Override
    public void submitOrderProfit(ChannelProfitOrderContext channelProfitOrderContext) {
        ProviderChannelConfigDO providerChannelConfigDO = channelProfitOrderContext.getProviderChannelConfig();
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        HeliPayConfig config = JsonUtils.parseObject(channelConfig, HeliPayConfig.class);
        PayOrderDO payOrderDO = channelProfitOrderContext.getPayOrderDO();
        try {
            List<PayOrderFundDto> payOrderFundDtos = channelProfitOrderContext.getPayOrderFundDtoList();

            DelayedSplitApplyReq splitApplyReq = new DelayedSplitApplyReq();
            splitApplyReq.setOrderId(payOrderDO.getPayOrderNo());
            splitApplyReq.setOriginalOrderId(payOrderDO.getPayOrderNo());
            splitApplyReq.setOriginalProductCode("APPPAY");
            List<DelayedSplitApplyReq.SplitBillRule> splitBillRuleList = processFund(payOrderDO, payOrderFundDtos);
            splitApplyReq.setRuleJson(splitBillRuleList);

            String requestBody = JsonUtils.toJsonString(splitApplyReq);
            log.info("合利宝 订单分润 请求参数:{}", requestBody);
            String url = yqtDomain(channelProfitOrderContext.isTest()) + HeliPayConstant.DELAYED_FUND_ORDER_APPLY;
            PayChannelRequestDO payChannelRequestDO = payChannelRequestDO(url, splitApplyReq, config, payOrderDO.getSubMchId());
            channelProfitOrderContext.setPayChannelRequest(payChannelRequestDO);
            payChannelRequestDO.setResponse(payChannelRequestDO.getResponse());
            log.info("合利宝 订单分润 返回参数:{}", payChannelRequestDO.getResponse());
            channelProfitOrderContext.setPayChannelRequest(payChannelRequestDO);
            channelProfitOrderContext.setRequestUrl(url);
            DelayedSplitCommonRes<String> commonRes = JsonUtils.parseObject(payChannelRequestDO.getResponse(), new TypeReference<>() {
            });

            String respDesc;
            PayOrderEnums.PAY_ORDER_FUND_STATUS payOrderFundStatus;
            if (!commonRes.isSuccess()) {
                payOrderFundStatus = PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL;
                respDesc = "合利宝 订单分润 请求失败,失败信息:" + commonRes.getMessage();
            } else {
                DelayedSplitApplyRes delayedSplitApplyRes = JsonUtils.parseObject(commonRes.getData(), DelayedSplitApplyRes.class);
                switch (delayedSplitApplyRes.getStatus()) {
                    case "SUCCESS":
                        respDesc = null;
                        payOrderFundStatus = PayOrderEnums.PAY_ORDER_FUND_STATUS.SUCCESS;
                        break;
                    case "FAILED":
                        payOrderFundStatus = PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL;
                        respDesc = "合利宝 订单分润 请求失败";
                        break;
                    default:
                        respDesc = null;
                        payOrderFundStatus = PayOrderEnums.PAY_ORDER_FUND_STATUS.FUNDING;
                        break;
                }
            }
            payOrderFundDtos.stream().forEach(payOrderFundDO -> {
                payOrderFundDO.setFundStatus(payOrderFundStatus);
                payOrderFundDO.setFailReason(respDesc);
            });
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("合利宝提交订单分润请求失败 message:{}", e.getMessage(), e);
            channelProfitOrderContext.setRemark("合利宝提交订单分润请求失败,失败信息:" + e.getMessage());
            channelProfitOrderContext.setProcessStatus(Boolean.FALSE);
        }
    }

    @Override
    public void queryOrderProfit(ChannelProfitOrderQueryContext channelProfitOrderQueryContext) {
        ProviderChannelConfigDO providerChannelConfigDO = channelProfitOrderQueryContext.getProviderChannelConfig();
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        HeliPayConfig config = JsonUtils.parseObject(channelConfig, HeliPayConfig.class);
        PayOrderDO payOrderDO = channelProfitOrderQueryContext.getPayOrderDO();
        try {
            List<OrderFundDetailDO> payOrderFundList = channelProfitOrderQueryContext.getPayOrderFunds();
            DelayedSplitQueryReq queryReq = new DelayedSplitQueryReq();
            queryReq.setOrderId(payOrderDO.getPayOrderNo());

            String requestBody = JsonUtils.toJsonString(queryReq);
            log.info("合利宝 订单分润查询 请求参数:{}", requestBody);
            String url = yqtDomain(channelProfitOrderQueryContext.isTest()) + HeliPayConstant.DELAYED_FUND_ORDER_QUERY;
            PayChannelRequestDO payChannelRequestDO = payChannelRequestDO(url, queryReq, config, payOrderDO.getSubMchId());
            payChannelRequestDO.setResponse(payChannelRequestDO.getResponse());
            log.info("合利宝 订单分润查询 返回参数:{}", payChannelRequestDO.getResponse());
            channelProfitOrderQueryContext.setPayChannelRequest(payChannelRequestDO);
            channelProfitOrderQueryContext.setRequestUrl(url);
            DelayedSplitCommonRes<String> commonRes = JsonUtils.parseObject(payChannelRequestDO.getResponse(), new TypeReference<>() {
            });
            List<OrderFundDetailDO> upPayOrderFundList = new ArrayList<>();
            if (!commonRes.isSuccess()) {
                channelProfitOrderQueryContext.setRemark("合利宝订单分润查询请求失败,失败信息:" + commonRes.getMessage());
                channelProfitOrderQueryContext.setProcessStatus(false);
                return;
            }
            DelayedSplitQueryRes respOrderInfo = JsonUtils.parseObject(commonRes.getData(), DelayedSplitQueryRes.class);
            List<DelayedSplitQueryRes.SplitBillRule> allotList = respOrderInfo.getRuleJson();
            Map<String, DelayedSplitQueryRes.SplitBillRule> allotListMap = allotList.stream()
                    .collect(Collectors.toMap(allotListBean -> allotListBean.getSplitBillMerchantNo(), Function.identity()));
            payOrderFundList.stream().forEach(payOrderFundDO -> {
                DelayedSplitQueryRes.SplitBillRule allotListBean = allotListMap.get(payOrderFundDO.getAccountNo());
                if (allotListBean != null && StringUtils.hasText(allotListBean.getSplitBillOrderStatus())) {
                    // 0-未开始结算,2-结算完成
                    switch (allotListBean.getSplitBillOrderStatus()) {
                        case "SUCCESS":
                            payOrderFundDO.setFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.SUCCESS.getCode());
                            if (StringUtils.hasText(respOrderInfo.getCompleteTime())) {
                                payOrderFundDO.setSuccessTime(DateUtils.shanghaiDate(respOrderInfo.getCompleteTime(), "yyyy-MM-dd HH:mm:ss"));
                            }
                            payOrderFundDO.setOrderNo(allotListBean.getSplitBillOrderNum());
                            break;
                        case "FAIL":
                            payOrderFundDO.setFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL.getCode());
                            if (StringUtils.hasText(respOrderInfo.getCompleteTime())) {
                                payOrderFundDO.setSuccessTime(DateUtils.shanghaiDate(respOrderInfo.getCompleteTime(), "yyyy-MM-dd HH:mm:ss"));
                            }
                            payOrderFundDO.setOrderNo(allotListBean.getSplitBillOrderNum());
                            break;
                        default:
                    }
                }
                payOrderFundDO.setFailReason(respOrderInfo.getStateDesc());
            });
//            channelProfitOrderQueryContext.setPayOrderFunds(upPayOrderFundList);
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("合利宝 订单分润查询请求失败 message:{}", e.getMessage(), e);
            channelProfitOrderQueryContext.setRemark("合利宝 订单分润查询请求失败,失败信息:" + e.getMessage());
            channelProfitOrderQueryContext.setProcessStatus(Boolean.FALSE);
        }
    }

    @Override
    public void submitRefundOrderProfit(ChannelProfitOrderContext channelProfitOrderContext) {
        ProviderChannelConfigDO providerChannelConfigDO = channelProfitOrderContext.getProviderChannelConfig();
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        HeliPayConfig config = JsonUtils.parseObject(channelConfig, HeliPayConfig.class);
        PayOrderDO payOrderDO = channelProfitOrderContext.getPayOrderDO();
        try {
            DelayedSplitReturnReq returnReq = new DelayedSplitReturnReq();
            returnReq.setRefundOrderId(payOrderDO.getPayOrderNo());
            returnReq.setOriginalOrderId(payOrderDO.getPayOrderNo());

            List<RefundOrderFundDto> payOrderFundList = channelProfitOrderContext.getRefundOrderFundDtoList();
            List<DelayedSplitReturnReq.RefundDetailRule> ruleJson = new ArrayList<>();
            for (RefundOrderFundDto orderFundDetailDO : payOrderFundList) {
                DelayedSplitReturnReq.RefundDetailRule rule = new DelayedSplitReturnReq.RefundDetailRule();
                rule.setMerchantNo(orderFundDetailDO.getAccountNo());
                rule.setRefundAmount(new BigDecimal(orderFundDetailDO.getAmount()));
                ruleJson.add(rule);
            }
            returnReq.setRuleJson(ruleJson);
            String requestBody = JsonUtils.toJsonString(returnReq);
            log.info("合利宝 订单分润退回 请求参数:{}", requestBody);
            String url = yqtDomain(channelProfitOrderContext.isTest()) + HeliPayConstant.DELAYED_FUND_ORDER_REFUND;
            PayChannelRequestDO payChannelRequest = payChannelRequestDO(url, returnReq, config, payOrderDO.getSubMchId());
            log.info("合利宝 订单分润退回 返回参数:{}", payChannelRequest.getResponse());
            channelProfitOrderContext.setPayChannelRequest(payChannelRequest);
            channelProfitOrderContext.setRequestUrl(url);
            DelayedSplitCommonRes<String> commonRes = JsonUtils.parseObject(payChannelRequest.getResponse(), new TypeReference<>() {
            });
            String respDesc;
            PayOrderEnums.PAY_ORDER_FUND_STATUS payOrderFundStatus;
            if (!commonRes.isSuccess()) {
                payOrderFundStatus = PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL;
                respDesc = "合利宝订单分润退回请求失败,失败信息:" + commonRes.getMessage();
            } else {
                DelayedSplitBackRes backRes = JsonUtils.parseObject(commonRes.getData(), DelayedSplitBackRes.class);
                switch (backRes.getStatus()) {
                    case "SUCCESS":
                        respDesc = null;
                        payOrderFundStatus = PayOrderEnums.PAY_ORDER_FUND_STATUS.SUCCESS;
                        break;
                    case "FAILED":
                        payOrderFundStatus = PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL;
                        respDesc = "合利宝订单分润退回请求失败 请求失败";
                        break;
                    default:
                        respDesc = null;
                        payOrderFundStatus = PayOrderEnums.PAY_ORDER_FUND_STATUS.FUNDING;
                        break;
                }
            }

            payOrderFundList.stream().forEach(payOrderFundDO -> {
                payOrderFundDO.setFundStatus(payOrderFundStatus);
                payOrderFundDO.setFailReason(respDesc);
            });
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("合利宝提交退款订单分润请求失败 message:{}", e.getMessage(), e);
            channelProfitOrderContext.setRemark("合利宝提交退款订单分润请求失败,失败信息:" + e.getMessage());
            channelProfitOrderContext.setProcessStatus(Boolean.FALSE);
        }
    }

    @Override
    public void queryRefundOrderProfit(ChannelProfitOrderQueryContext channelProfitOrderQueryContext) {
        ProviderChannelConfigDO providerChannelConfigDO = channelProfitOrderQueryContext.getProviderChannelConfig();
        String channelConfig = providerChannelConfigDO.getParamsConfig();
        // 配置转换
        HeliPayConfig config = JsonUtils.parseObject(channelConfig, HeliPayConfig.class);
        PayOrderDO payOrderDO = channelProfitOrderQueryContext.getPayOrderDO();
        try {
            DelayedSplitReturnQueryReq queryReq = new DelayedSplitReturnQueryReq();
            queryReq.setRefundOrderId(payOrderDO.getPayOrderNo());

            String requestBody = JsonUtils.toJsonString(queryReq);
            log.info("合利宝 订单分润退回查询 请求参数:{}", requestBody);
            String url = yqtDomain(channelProfitOrderQueryContext.isTest()) + HeliPayConstant.DELAYED_FUND_ORDER_REFUND_QUERY;
            PayChannelRequestDO payChannelRequestDO = payChannelRequestDO(url, queryReq, config, payOrderDO.getSubMchId());
            payChannelRequestDO.setResponse(payChannelRequestDO.getResponse());
            log.info("合利宝 订单分润退回查询 返回参数:{}", payChannelRequestDO.getResponse());
            channelProfitOrderQueryContext.setPayChannelRequest(payChannelRequestDO);
            channelProfitOrderQueryContext.setRequestUrl(url);

            DelayedSplitCommonRes<String> commonRes = JsonUtils.parseObject(payChannelRequestDO.getResponse(), new TypeReference<>() {
            });
            List<OrderFundDetailDO> upPayOrderFundList = new ArrayList<>();
            if (!commonRes.isSuccess()) {
                channelProfitOrderQueryContext.setRemark("合利宝订单分润退回查询请求失败,失败信息:" + commonRes.getMessage());
                channelProfitOrderQueryContext.setProcessStatus(false);
                return;
            }
            List<OrderFundDetailDO> payOrderFundList = channelProfitOrderQueryContext.getRefundOrderFunds();
            DelayedSplitBackQueryRes respOrderInfo = JsonUtils.parseObject(commonRes.getData(), DelayedSplitBackQueryRes.class);
            List<DelayedSplitBackQueryRes.SplitBillRule> allotList = respOrderInfo.getRuleJson();
            Map<String, DelayedSplitBackQueryRes.SplitBillRule> allotListMap = allotList.stream()
                    .collect(Collectors.toMap(allotListBean -> allotListBean.getMerchantNo(), Function.identity()));
            payOrderFundList.stream().forEach(payOrderFundDO -> {
                DelayedSplitBackQueryRes.SplitBillRule allotListBean = allotListMap.get(payOrderFundDO.getAccountNo());
                if (allotListBean != null && StringUtils.hasText(allotListBean.getRefundStatus())) {
                    // 0-未开始结算,2-结算完成
                    switch (allotListBean.getRefundStatus()) {
                        case "SUCCESS":
                            payOrderFundDO.setFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.SUCCESS.getCode());
                            if (StringUtils.hasText(respOrderInfo.getCompleteTime())) {
                                payOrderFundDO.setSuccessTime(DateUtils.shanghaiDate(respOrderInfo.getCompleteTime(), "yyyy-MM-dd HH:mm:ss"));
                            }
                            break;
                        case "FAIL":
                            payOrderFundDO.setFundStatus(PayOrderEnums.PAY_ORDER_FUND_STATUS.FAIL.getCode());
                            if (StringUtils.hasText(respOrderInfo.getCompleteTime())) {
                                payOrderFundDO.setSuccessTime(DateUtils.shanghaiDate(respOrderInfo.getCompleteTime(), "yyyy-MM-dd HH:mm:ss"));
                            }
                            break;
                        default:
                    }
                }
                payOrderFundDO.setFailReason(respOrderInfo.getStateDesc());
            });
            channelProfitOrderQueryContext.setPayOrderFunds(upPayOrderFundList);
        }
        catch (Exception e) {
            e.printStackTrace();
            log.error("合利宝订单分润退回查询请求失败 message:{}", e.getMessage(), e);
            channelProfitOrderQueryContext.setRemark("合利宝订单分润退回查询请求失败,失败信息" + e.getMessage());
            channelProfitOrderQueryContext.setProcessStatus(Boolean.FALSE);
        }
    }

    @Override
    public void finishAcctOrder(ChannelProfitOrderContext channelProfitOrderContext) {

    }

    @Override
    public CommonResult<String> acctStatusParse(HttpServletRequest request, ChannelProfitOrderContext channelProfitOrderContext) {
        return null;
    }

    @Override
    public void settleConfirm(ChannelProfitOrderContext channelProfitOrderContext) {

    }

    List<DelayedSplitApplyReq.SplitBillRule> processFund(PayOrderDO payOrderDO, List<PayOrderFundDto> accounts) {
        List<DelayedSplitApplyReq.SplitBillRule> separateOrderDetails = new ArrayList<>();
        accounts.stream().forEach(account -> {
            DelayedSplitApplyReq.SplitBillRule separateOrderDetailList = new DelayedSplitApplyReq.SplitBillRule();
            separateOrderDetailList.setSplitBillMerchantNo(account.getAccountNo());
            separateOrderDetailList.setSplitBillAmount(new BigDecimal(account.getAmount()));
            separateOrderDetails.add(separateOrderDetailList);
        });
        return separateOrderDetails;
    }

    public HeliPayAppChannelParams easyPayAppChannelParams(MerchantAppChannelDO merchantAppChannelDO) {
        return JsonUtils.parseObject(merchantAppChannelDO.getAppConfigJson(), HeliPayAppChannelParams.class);
    }

    PayChannelRequestDO payChannelRequestDO(String url, Object params, HeliPayConfig heliPayConfig, String subMchId) throws Exception {
        PayChannelRequestDO payChannelRequestDO = new PayChannelRequestDO();
        payChannelRequestDO.setRequestUrl(url);
        String requestBody = JsonUtils.toJsonString(params);
        payChannelRequestDO.setRequestParams(requestBody);
        SignAndEncryptedContent signAndEncryptedContent = HeliPaySignUtil.fundSign(heliPayConfig, requestBody);
        Map<String,Object> map = new HashMap<>();
        map.put("data", signAndEncryptedContent.getEncryptedStr());
        map.put("customerNumber", subMchId);
        map.put("encryptionKey", signAndEncryptedContent.getEncryptionKey());
        map.put("signType", "SM3WITHSM2");
        map.put("sign", signAndEncryptedContent.getSign());
        map.put("timestamp", DateUtils.getDateString(DatePattern.PURE_DATETIME_PATTERN));
        map.put("version", "1.0");
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type" , "application/json");
        log.info("合利宝 订单分润 最终请求参数:{}", JsonUtils.toJsonString(map));
        String post = okHttpTemplate.post(url, JsonUtils.toJsonString(map), headers);
        payChannelRequestDO.setResponse(post);
        return payChannelRequestDO;
    }

    public String yqtDomain(boolean test) {
        return test ? HeliPayConstant.DELAYED_SPLIT_DOMAIN_TEST : HeliPayConstant.DELAYED_SPLIT_DOMAIN_PROD;
    }

}
